[dmt] feat: add remote image linting - #470
Merged
Merged
Conversation
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com> Co-authored-by: Smyslov Maxim <maksim.smyslov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
fuldaxxx
force-pushed
the
feat/add-remote-scope
branch
from
September 2, 2026 07:21
80e7513 to
36a47d0
Compare
fuldaxxx
marked this pull request as ready for review
September 2, 2026 07:32
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
fuldaxxx
marked this pull request as draft
September 2, 2026 08:18
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
fuldaxxx
marked this pull request as ready for review
September 2, 2026 14:16
fuldaxxx
marked this pull request as draft
September 2, 2026 14:20
Route static and remote sources through the manager for consistent summaries, metrics, and linter filters. Resolve credentials from flags, environment variables, Docker config, or anonymous access. Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
Signed-off-by: Ruslan Gorbunov <ruslan.gorbunov@flant.com>
fuldaxxx
marked this pull request as ready for review
September 3, 2026 09:10
ldmonster
approved these changes
Sep 3, 2026
ipaqsa
approved these changes
Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds
dmt lint remote— linting a module as it was published, not as it sits in a working tree:One reference addresses both images a release produces: the bundle at
<repo>:<tag>and the release metadata at<repo>/release:<tag>.dmtpulls each, unpacks it into a temporary directory and lints it as a module, printing findings and the summary exactly the way a local run does.This is built on the scope mechanism from #468. Two scopes join
static:staticglobal.linters-settingsbundle<repo>:<tag>remote.bundlerelease<repo>/release:<tag>remote.releaseOne run path, two sources
Where the modules come from is now the only thing that differs between the two commands.
internal/managerowns everything after the modules exist — running the linters, printing, statistics, metrics, the--linterfilter — behind amanager.Source:internal/staticlintimplements it over a directory (the module discovery, values loading andvalidateModulemoved there out of the manager),internal/remotelintover a registry reference.cmd/dmt'srunLinttakes aSourceand is otherwise the same function for both. What that buys:--linterfilter and the metrics send it did not have — the remote path used to collect findings metrics throughIncDmtLinterErrorsCountand then never callSend, which looks like a working run right up until nobody can find its data. Both paths now end atmetrics.Flush, which records the config sections the run actually linted with (linters-settings, orremote.bundle+remote.release);Targetis a module and a scope, so a remote run reads one module from two images and the summary still counts one module (ModuleID), while the findings of the two images stay apart (ObjectIDcarries the scope name);--fixis rejected forlint remote: the tree is an extracted image thrown away at the end of the run, so a fix would only drop a finding from the report.Per-scope configuration is independent
The two images carry different files and are linted by different rules, so each gets its own section of
.dmtlint.yaml, and nothing fromlinters-settingsreaches a remote scope. A section left out means the built-in severities, not the ones the source tree happens to be tuned to:Scope.Settingsis the only place that mapping lives, andremotelinthands the branch it returns tomodules.NewRemoteModulerather than the whole root config — a remote scope has no way to reach the source tree's settings even by accident.New rules
Both check presence rather than content — presence is a property of the scope, not of the file, so the rules that parse those files keep returning quietly when one is absent:
module/release-layout—module.yaml,version.json,changelog.yamlin the release image root;module/bundle-layout—.helmignore,Chart.yaml,images_digests.json,module.yaml, plus thecharts/,docs/,openapi/andtemplates/directories. The list is the intersection of eight published CE bundles, not everything they carry:crds/,hooks/,monitoring/and.werf/appear in some and not others, so requiring them would fail modules that legitimately have nothing to put there.Findings from a remote scope name module-relative paths. The extraction directory is removed once the run is over, so a finding that named the absolute path would point at nothing —
docs/readmewas changed the same way.Credentials
--login/--password→DMT_REGISTRY_LOGIN/DMT_REGISTRY_PASSWORD→ Docker config (d8 dk cr login) → anonymous. Each field falls back on its own, so a CI job can keep the login in the pipeline definition and the password in a secret — which is also the reason the environment variables exist: a password passed as a flag lands in the process list and in the job's own command echo.Invariant the scope tables must keep
A remote module comes from
modules.NewRemoteModule, which skips the chart load and the render, soGetChart,GetObjectStoreandGetValuesare nil there. Thereleaseandbundletables must not ask for a rule that reads them —TestRemoteScopesRunOverAnUnpackedImageruns both scopes over a real unpacked layout to catch a rule ID that cannot survive there, and asserts that no finding names the extraction directory.go.modpinsdocker/clito v27.1.1 viareplace:deckhouse/pkg/registrypulls v29, whosetypes.AuthConfigno longer converts fromdocker/docker'sregistry.AuthConfig, which breaksoras.land/oras-goreached throughwerf/nelm.Based on #422 — the
--remoteflag, the extraction flow and thechangelogrule come from there; this PR rebuilds them on top of the scope mechanism, adds the independent per-scope configuration and the layout rules, unifies the two run paths behindmanager.Source, and hardens the extraction and the failure paths.Why do we need it, and what problem does it solve?
Modules ship as OCI artifacts, but
dmtcould only lint a filesystem checkout. A release pipeline that has the built artifact and nothing else had no way to validate it, and neither did anyone auditing a module already in a registry — the artifact that actually ships was the one thingdmtnever looked at.The gap is not only "the same checks, remotely". A published module is a different shape from its source tree: the bundle is a rendered Helm package with
images_digests.jsonand.helmignore, the release image is metadata Deckhouse reads to decide whether to install a version, and neither looks like the directory the module was built from. Some source-tree checks are meaningless there (markdownlintover rendered docs), and some checks matter only there (changelog.yamlandversion.jsonare optional in a repo and mandatory in a release image). That is why this lands as two scopes with their own rule tables and their own config sections, rather than as a flag that reruns the static pass over an unpacked directory: a packaging mistake — a missingchangelog.yaml, a bundle withoutimages_digests.json— is exactly the class of bug that is invisible in the source tree and breaks on install.